import React from 'react'
import { PointModel } from 'react-js-diagrams'
import {FlowConst} from "../../Const";

export class DirectedLinkWidget extends React.Component {
    static defaultProps = {
        color: 'black',
        width: 3,
        link:null,
        engine: null,
        smooth: false,
        diagramEngine: null,
    };

    constructor (props) {
        super(props)
        this.state = {
            selected: false,
        }
    }

    generatePoint (pointIndex) {
        const { link } = this.props
        const uiCircleProps = {
            className: `point pointui${(link.points[pointIndex].isSelected() ? ' selected' : '')}`,
            cx: link.points[pointIndex].x,
            cy: link.points[pointIndex].y,
            r: 5,
        }
        const circleProps = {
            className: 'point',
            'data-linkid': link.id,
            'data-id': link.points[pointIndex].id,
            cx: link.points[pointIndex].x,
            cy: link.points[pointIndex].y,
            r: 15,
            opacity: 0,
            onMouseLeave: () => this.setState({ selected: false }),
            onMouseEnter: () => this.setState({ selected: true }),
        }

        return (
            <g key={`point-${link.points[pointIndex].id}`}>
                <circle {...uiCircleProps}/>
                <circle {...circleProps}/>
            </g>
        )
    }

    generateLink (extraProps, needTriangleMarkerEnd = false) {
        const { link, width, color } = this.props
        const { selected } = this.state
        const arrowId = "arrowMarker-" + link.getID()
        const lineMarkerEndRefId = "url(#" + arrowId + ")"
        const arrow =(
            <defs>
                <marker id={arrowId} markerWidth="20" markerHeight="20" refX="0" refY="6" orient="auto" markerUnits="userSpaceOnUse">
                    <path d="M 0 0 L 0 12 L 20 6 z" style={{fill:"#000000"}} />
                </marker>
            </defs>
        )

        const bottom = (
            <path
                className={(selected || link.isSelected()) ? 'selected' : ''}
                strokeWidth={width}
                stroke={color}
                {...extraProps}
            />
        )


        const top = (
            <path
                strokeLinecap={'round'}
                data-linkid={link.getID()}
                stroke={color}
                strokeOpacity={selected ? 0.1 : 0}
                strokeWidth={20}
                onMouseLeave={() => this.setState({ selected: false })}
                onMouseEnter={() => this.setState({ selected: true })}
                markerEnd={needTriangleMarkerEnd ? lineMarkerEndRefId : ""}
                onContextMenu={event => {
                    event.preventDefault()
                    this.props.link.remove()
                }}
                {...extraProps}
            />
        )




        return (
            <g key={`link-${extraProps.id}`}>
                {arrow}
                {bottom}
                {top}
            </g>
        )
    }

    drawLine () {
        const { link, diagramEngine, pointAdded } = this.props
        const { points, sourceCenterPoint, targetCenterPoint } = link
        const paths = []

        let pointSource = points[0]
        let pointTarget = points[1]

        let inflexionPoint = this.getInflexionPoint(sourceCenterPoint , targetCenterPoint, pointSource, pointTarget)

        let pointTargetReserveSpaceForArrow = this.getTargetPointToReserveSpaceForArrow(inflexionPoint, pointTarget)

        const needTriangleMarkerEnd = true

        paths.push(this.generateLink({
            id: 0,
            onMouseDown: (event) => {
                if (!event.shiftKey) {
                    var point = new PointModel(link, diagramEngine.getRelativeMousePoint(event))
                    point.setSelected(true)
                    this.forceUpdate()
                    link.addPoint(point, 1)
                    pointAdded(point, event)
                }
            },
            d: ` M${pointSource.x} ${pointSource.y} L${inflexionPoint.x} ${inflexionPoint.y} ${pointTargetReserveSpaceForArrow.x} ${pointTargetReserveSpaceForArrow.y}` // eslint-disable-line
        }, needTriangleMarkerEnd ))

        if (link.targetPort === null) {
            paths.push(this.generatePoint(1))
        }

        return paths
    }

    getTargetPointToReserveSpaceForArrow (inflexionPoint, originalTargetPoint){

        if(!inflexionPoint){
            return originalTargetPoint
        }
        let xDistance = originalTargetPoint.x - inflexionPoint.x
        let yDistance = originalTargetPoint.y - inflexionPoint.y
        let distance = Math.sqrt(xDistance * xDistance + yDistance * yDistance)

        return {
            x : originalTargetPoint.x - FlowConst.ARROW_SIZE * xDistance / distance,
            y : originalTargetPoint.y - FlowConst.ARROW_SIZE * yDistance / distance,
        }
    }

    getInflexionPoint (sourceCenterPoint, targetCenterPoint, sourcePoint, targetPoint){
        let inflexionX = sourcePoint.x
        let inflexionY = sourcePoint.y

        let halfWidth = Math.max(Math.abs(sourcePoint.x - sourceCenterPoint.x), Math.abs(targetPoint.x - targetCenterPoint.x))
        let halfHeight = Math.max(Math.abs(sourcePoint.y - sourceCenterPoint.y), Math.abs(targetPoint.y - targetCenterPoint.y))
        halfWidth = Math.max(halfWidth,FlowConst.DEFAULT_HALF_COMPONENT_SIZE )
        halfHeight = Math.max(halfHeight,FlowConst.DEFAULT_HALF_COMPONENT_SIZE )


        if(Math.abs(sourceCenterPoint.x - targetCenterPoint.x) > FlowConst.ARROW_SIZE + halfWidth){
            inflexionX = targetPoint.x
        }
        if(Math.abs(sourceCenterPoint.y - targetCenterPoint.y) <= FlowConst.ARROW_SIZE + halfHeight){
            inflexionX = sourcePoint.x
        }

        return {
            x: inflexionX,
            y: inflexionY,
        }
    }

    drawAdvancedLine () {
        const { link, smooth, diagramEngine, pointAdded } = this.props
        const { points,sourceCenterPoint,targetCenterPoint } = link
        const ds = []

        let pointSource = points[0]
        let pointTarget = points[points.length - 1]


        let pointTargetReserveSpaceForArrow = this.getTargetPointToReserveSpaceForArrow(points[points.length - 2], pointTarget)

        if (smooth) {
            ds.push(
                ` M${points[0].x} ${points[0].y} C ${points[0].x + 50} ${points[0].y} ${points[1].x} ${points[1].y} ${points[1].x} ${points[1].y}` // eslint-disable-line
            )

            let i
            for (i = 1; i < points.length - 2; i++) {
                ds.push(` M ${points[i].x} ${points[i].y} L ${points[i + 1].x} ${points[i + 1].y}`)
            }

            ds.push(
                ` M${points[i].x} ${points[i].y} C ${points[i].x} ${points[i].y} ${points[i + 1].x - 50} ${points[i + 1].y} ${pointTargetReserveSpaceForArrow.x} ${pointTargetReserveSpaceForArrow.y}` // eslint-disable-line
            )
        } else {
            for (let i = 0; i < points.length - 2; i++) {
                ds.push(` M ${points[i].x} ${points[i].y} L ${points[i + 1].x} ${points[i + 1].y}`)
            }

            ds.push(` M ${points[points.length - 2].x} ${points[points.length - 2].y} L ${pointTargetReserveSpaceForArrow.x} ${pointTargetReserveSpaceForArrow.y}`)
        }

        const paths = ds.map((data, index) => this.generateLink({
            id: index,
            d: data,
            'data-linkid': link.id,
            'data-point': index,
            onMouseDown: event => {
                if (!event.shiftKey) {
                    const point = new PointModel(link, diagramEngine.getRelativeMousePoint(event))
                    point.setSelected(true)
                    this.forceUpdate()
                    link.addPoint(point, index + 1)
                    pointAdded(point, event)
                }
            },
        }, index === ds.length - 1))

        // Render the circles
        for (let i = 1; i < points.length - 1; i++) {
            paths.push(this.generatePoint(i))
        }

        if (link.targetPort === null) {
            paths.push(this.generatePoint(points.length - 1))
        }

        return paths
    }

    render () {
        const { points } = this.props.link
        let paths = []

        // Draw the line
        if (points.length === 2) {
            paths = this.drawLine()
        } else {
            paths = this.drawAdvancedLine()
        }

        return (
            <g>
                {paths}
            </g>
        )
    }
}
